package com.javaear.beangenerator.processor;

import com.javaear.beangenerator.Beaner;
import com.javaear.beangenerator.Context;
import com.javaear.beangenerator.Fielder;
import com.javaear.beangenerator.tools.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Jaxb processor
 *
 * @author aooer
 */
public class JaxbReverseProcessor extends AbstractProcessor {

    /**
     * jaxb xml document
     */
    private Document document;

    public void init(Context context) {
        String docPath;
        Assert.notNull(docPath = context.getString("jaxb_param"));
        try {
            if (document == null)
                document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(docPath));
            Assert.notNull(document.getChildNodes());
            Assert.notNull(document.getChildNodes().item(0));
            Assert.notNull(document.getChildNodes().item(0).getChildNodes());
        } catch (Exception e) {
            throw new RuntimeException("渲染xml文件bean初始化异常", e);
        }
    }

    public Beaner process() {
        NodeList nodeList = document.getChildNodes();
        return loopNode(nodeList.item(0));
    }

    /**
     * loop the node
     * if has child
     * add beaner child
     *
     * @param node node
     * @return Beaner
     */
    private static Beaner loopNode(Node node) {
        NodeList nodeList = node.getChildNodes();
        List<Fielder> fields = new ArrayList<Fielder>();
        List<Beaner> children = new ArrayList<Beaner>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node nodeItem = nodeList.item(i);
            if (isFilterNode(nodeItem)) continue;
            if (isFilterNodeList(nodeItem.getChildNodes())) {
                children.add(loopNode(nodeItem));
                fields.add(new Fielder(nodeItem.getNodeName(), nodeItem.getNodeName()));
            } else
                fields.add(new Fielder(nodeItem.getNodeName(), "String"));
        }
        return new Beaner(node.getNodeName(), fields, children);
    }

    /**
     * filter when node is not need element
     *
     * @param node node
     * @return is filter type
     */
    private static boolean isFilterNode(Node node) {
        Character firstChar = node.getNodeName().charAt(0);
        return node.getNodeType() != 1 ||//dom node
                !(Character.isLetter(firstChar) || firstChar == '_' || firstChar == '$') ||
                node.getNodeName().contains(":") ||
                node.getNodeName().contains("+") ||
                node.getNodeName().contains(" ")
                ;
    }

    /**
     * check has children or not
     *
     * @param nodelist nodelist
     * @return is or not
     */
    private static boolean isFilterNodeList(NodeList nodelist) {
        if (nodelist == null) return false;
        for (int i = 0; i < nodelist.getLength(); i++)
            if (!isFilterNode(nodelist.item(i)))
                return true;
        return false;
    }
}
